"""Error handling utilities with retry and fallback mechanisms."""
import time
import logging
from typing import Callable, Any, Optional, TypeVar, Dict
from functools import wraps
import requests

logger = logging.getLogger(__name__)

T = TypeVar('T')


class NetworkError(Exception):
    """Network-related error."""
    pass


class APIError(Exception):
    """API-related error."""
    pass


class CacheMissError(Exception):
    """Cache miss error."""
    pass


def retry_with_exponential_backoff(
    max_retries: int = 3,
    initial_delay: float = 1.0,
    backoff_factor: float = 2.0,
    exceptions: tuple = (requests.RequestException, NetworkError, APIError)
):
    """Decorator for retrying functions with exponential backoff.
    
    Args:
        max_retries: Maximum number of retry attempts
        initial_delay: Initial delay in seconds
        backoff_factor: Multiplier for delay after each retry
        exceptions: Tuple of exceptions to catch and retry
        
    Returns:
        Decorated function
    """
    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @wraps(func)
        def wrapper(*args, **kwargs) -> T:
            delay = initial_delay
            last_exception = None
            
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt < max_retries - 1:
                        logger.warning(
                            f"Attempt {attempt + 1}/{max_retries} failed for {func.__name__}: {str(e)}. "
                            f"Retrying in {delay}s..."
                        )
                        time.sleep(delay)
                        delay *= backoff_factor
                    else:
                        logger.error(
                            f"All {max_retries} attempts failed for {func.__name__}: {str(e)}"
                        )
            
            # All retries exhausted
            raise last_exception
        
        return wrapper
    return decorator


def with_cache_fallback(
    cache_key_func: Callable[..., str],
    cache_getter: Callable[[str], Optional[Any]],
    cache_setter: Callable[[str, Any, int], None],
    cache_duration: int = 1800
):
    """Decorator for using cache as fallback when function fails.
    
    Args:
        cache_key_func: Function to generate cache key from args
        cache_getter: Function to get value from cache
        cache_setter: Function to set value in cache
        cache_duration: Cache duration in seconds
        
    Returns:
        Decorated function
    """
    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @wraps(func)
        def wrapper(*args, **kwargs) -> T:
            # Generate cache key
            cache_key = cache_key_func(*args, **kwargs)
            
            try:
                # Try to execute function
                result = func(*args, **kwargs)
                
                # Cache successful result
                try:
                    cache_setter(cache_key, result, cache_duration)
                except Exception as e:
                    logger.warning(f"Failed to cache result for {func.__name__}: {str(e)}")
                
                return result
                
            except Exception as e:
                logger.warning(
                    f"Function {func.__name__} failed: {str(e)}. Attempting cache fallback..."
                )
                
                # Try to get from cache
                cached_result = cache_getter(cache_key)
                
                if cached_result is not None:
                    logger.info(f"Using cached result for {func.__name__}")
                    return cached_result
                else:
                    logger.error(f"No cached result available for {func.__name__}")
                    raise CacheMissError(f"Function failed and no cache available: {str(e)}")
        
        return wrapper
    return decorator


def safe_api_call(
    url: str,
    method: str = 'GET',
    params: Optional[Dict] = None,
    json_data: Optional[Dict] = None,
    headers: Optional[Dict] = None,
    timeout: int = 10
) -> requests.Response:
    """Make a safe API call with proper error handling.
    
    Args:
        url: API endpoint URL
        method: HTTP method (GET, POST, etc.)
        params: Query parameters
        json_data: JSON body data
        headers: HTTP headers
        timeout: Request timeout in seconds
        
    Returns:
        Response object
        
    Raises:
        NetworkError: If network request fails
        APIError: If API returns error status
    """
    try:
        response = requests.request(
            method=method,
            url=url,
            params=params,
            json=json_data,
            headers=headers,
            timeout=timeout
        )
        
        # Check for HTTP errors
        if response.status_code >= 400:
            error_msg = f"API returned status {response.status_code}"
            try:
                error_detail = response.json()
                error_msg += f": {error_detail}"
            except:
                error_msg += f": {response.text[:200]}"
            
            raise APIError(error_msg)
        
        return response
        
    except requests.Timeout as e:
        raise NetworkError(f"Request timeout: {str(e)}")
    except requests.ConnectionError as e:
        raise NetworkError(f"Connection error: {str(e)}")
    except requests.RequestException as e:
        raise NetworkError(f"Request failed: {str(e)}")


def get_user_friendly_error_message(error: Exception) -> str:
    """Convert technical error to user-friendly message.
    
    Args:
        error: Exception object
        
    Returns:
        User-friendly error message in Chinese
    """
    error_type = type(error).__name__
    error_str = str(error).lower()
    
    # Network errors
    if isinstance(error, (NetworkError, requests.ConnectionError)):
        return "网络连接失败，请检查您的网络设置"
    
    if isinstance(error, requests.Timeout):
        return "请求超时，请稍后重试"
    
    # API errors
    if isinstance(error, APIError):
        if '401' in error_str or 'unauthorized' in error_str:
            return "认证失败，请重新登录"
        elif '403' in error_str or 'forbidden' in error_str:
            return "没有访问权限"
        elif '404' in error_str or 'not found' in error_str:
            return "请求的资源不存在"
        elif '429' in error_str or 'rate limit' in error_str:
            return "请求过于频繁，请稍后重试"
        elif '500' in error_str or '502' in error_str or '503' in error_str:
            return "服务暂时不可用，请稍后重试"
        else:
            return "服务请求失败，请稍后重试"
    
    # Cache errors
    if isinstance(error, CacheMissError):
        return "服务暂时不可用，请稍后重试"
    
    # Generic errors
    if 'timeout' in error_str:
        return "操作超时，请稍后重试"
    elif 'connection' in error_str:
        return "连接失败，请检查网络"
    elif 'permission' in error_str or 'denied' in error_str:
        return "权限不足"
    else:
        return "操作失败，请稍后重试"


class ErrorResponse:
    """Standardized error response."""
    
    @staticmethod
    def create(error: Exception, include_details: bool = False) -> Dict:
        """Create standardized error response.
        
        Args:
            error: Exception object
            include_details: Whether to include technical details
            
        Returns:
            Error response dictionary
        """
        response = {
            'success': False,
            'error': {
                'message': get_user_friendly_error_message(error),
                'type': type(error).__name__
            }
        }
        
        if include_details:
            response['error']['details'] = str(error)
        
        return response
